package library.admin.service.impl;

import library.admin.domain.dto.PageDTO;
import library.admin.domain.po.Book;
import library.admin.domain.po.BookCopies;
import library.admin.domain.po.BorrowRecord;
import library.admin.domain.po.User;
import library.admin.domain.query.BookPageQuery;
import library.admin.domain.request.AddBookCopiesRequest;
import library.admin.domain.request.SaveBookRequest;
import library.admin.domain.request.UpdateBookRequest;
import library.admin.domain.vo.BookBorrowedOverviewVO;
import library.admin.domain.vo.BookCopiesVO;
import library.admin.domain.vo.BookVO;
import library.admin.enums.BookStatus;
import library.admin.mapper.BooksMapper;
import library.admin.service.IBookCopiesService;
import library.admin.service.IBooksService;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 
 *
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class BooksServiceImpl extends ServiceImpl<BooksMapper, Book> implements IBooksService {
    private final IBookCopiesService bookCopiesService;

    /**
     * 通过分页查询获取图书借阅情况概览
     *
     * @param pageQuery 分页及其条件
     * @return 分页结果
     */
    @Override
    public PageDTO<BookBorrowedOverviewVO> booksOverviewByPage(BookPageQuery pageQuery) {
        Integer id = pageQuery.getId();
        String title = pageQuery.getTitle();
        String author = pageQuery.getAuthor();
        String isbn = pageQuery.getIsbn();
        String category = pageQuery.getCategory();
        LocalDate minPublishedDate = pageQuery.getMinPublishedDate();
        LocalDate maxPublishedDateBegin = pageQuery.getMaxPublishedDate();
        Page<Book> page = pageQuery.toMpPageDefaultOrderByUpdateTime();
        lambdaQuery()
                .like(title != null, Book::getTitle, title)
                .like(author != null, Book::getAuthor, author)
                .like(category != null, Book::getCategory, category)
                .eq(id != null, Book::getId, id)
                .eq(isbn != null, Book::getIsbn, isbn)
                .ge(minPublishedDate != null, Book::getPublishedDate, minPublishedDate)
                .le(maxPublishedDateBegin != null, Book::getPublishedDate, maxPublishedDateBegin)
                .page(page);
        return PageDTO.of(page, book -> {
            BookBorrowedOverviewVO bookBorrowedOverviewVO = new BookBorrowedOverviewVO();
            bookBorrowedOverviewVO.setId(book.getId());
            bookBorrowedOverviewVO.setTitle(book.getTitle());
            bookBorrowedOverviewVO.setCopiesNumber(queryCopiesNumberById(book.getId()));
            bookBorrowedOverviewVO.setBorrowedCopiesNumber(queryBorrowedCopiesNumberById(book.getId()));
            bookBorrowedOverviewVO.setBorrowedNumber(queryBorrowedNumberById(book.getId()));
            bookBorrowedOverviewVO.setAuthor(book.getAuthor());
            bookBorrowedOverviewVO.setCategory(book.getCategory());
            bookBorrowedOverviewVO.setCoverUrl(book.getCoverUrl());
            return bookBorrowedOverviewVO;
        });
    }

    /**
     * 通过图书id获取图书复本id集合
     *
     * @param bookId 图书id
     * @return 复本id集合
     */
    @Override
    public List<Long> queryCopiesIdsById(Long bookId) {
        List<BookCopies> bookCopies = Db.lambdaQuery(BookCopies.class)
                .select(BookCopies::getId)
                .eq(BookCopies::getBookId, bookId)
                .list();
        return new ArrayList<>(bookCopies.stream().map(BookCopies::getId).toList());
    }

    /**
     * 通过图书id获取借出的图书复本数量
     *
     * @param bookId 图书id
     * @return 借出的图书复本数量
     */
    @Override
    public Long queryBorrowedCopiesNumberById(Long bookId) {
        if (queryCopiesIdsById(bookId).isEmpty()) {
            return 0L;
        }
        return Db.lambdaQuery(BorrowRecord.class)
                .in(BorrowRecord::getBookCopiesId, queryCopiesIdsById(bookId))
                .eq(BorrowRecord::getStatus, BookStatus.BORROWING)
                .count();
    }

    /**
     * 通过图书id获取图书借出次数
     *
     * @param bookId 图书id
     * @return 图书借出次数
     */
    @Override
    public Long queryBorrowedNumberById(Long bookId) {
        if (queryCopiesIdsById(bookId).isEmpty()) {
            return 0L;
        }
        return Db.lambdaQuery(BorrowRecord.class)
                .in(BorrowRecord::getBookCopiesId, queryCopiesIdsById(bookId))
                .count();
    }

    /**
     * 通过图书id获取图书复本数量
     *
     * @param bookId 图书id
     * @return 图书复本数量
     */
    @Override
    public Long queryCopiesNumberById(Long bookId) {
        if (queryCopiesIdsById(bookId).isEmpty()) {
            return 0L;
        }
        return Db.lambdaQuery(BookCopies.class).eq(BookCopies::getBookId, bookId).count();
    }


    @Transactional
    @Override
    public Long save(SaveBookRequest saveBookRequest) {
        if (existsIsbn(saveBookRequest.getIsbn())) {
            throw new IllegalArgumentException("ISBN: " + saveBookRequest.getIsbn() + " 与其它图书重复");
        }
        Book book = BeanUtil.copyProperties(saveBookRequest, Book.class);
        if (!save(book)) {
            throw new RuntimeException("Save book error");
        }
        Long copiesNumber = saveBookRequest.getCopiesNumber();
        if (copiesNumber != null && copiesNumber > 0) {
            List<BookCopies> bookCopies = new ArrayList<>();
            for (int i = 0; i < saveBookRequest.getCopiesNumber(); i++) {
                BookCopies copies = new BookCopies();
                copies.setBookId(book.getId());
                bookCopies.add(copies);
            }
            Db.saveBatch(bookCopies);
        }
        return book.getId();
    }

    @Override
    public boolean existsIsbn(String isbn) {
        return baseMapper.countIsbn(isbn) > 0;
    }

    @Override
    public List<BookCopiesVO> queryCopiesDetails(Long bookId) {
        List<Long> copiesIds = queryCopiesIdsById(bookId);
        if (copiesIds.isEmpty()) {
            return Collections.emptyList();
        }
        List<BorrowRecord> borrowRecords = Db.lambdaQuery(BorrowRecord.class)
                .in(BorrowRecord::getBookCopiesId, copiesIds)
                .eq(BorrowRecord::getStatus, BookStatus.BORROWING)
                .list();
        ArrayList<BookCopiesVO> copiesVOS = new ArrayList<>();
        //复本没有借阅记录
        if (borrowRecords.isEmpty()) {
            for (Long copyId : copiesIds) {
                copiesVOS.add(BookCopiesVO.of(copyId));
            }
            return copiesVOS;
        }
        log.debug("borrowRecords: {}", borrowRecords);
        List<Long> userIds = borrowRecords.stream()
                .map(BorrowRecord::getUserId)
                .toList();
        List<User> borrowUsers = Db.lambdaQuery(User.class)
                .in(User::getId, userIds)
                .list();
        log.debug("borrowUsers: {}", borrowUsers);
        log.debug("borrowRecords.size(): {}", borrowRecords.size());

        for (int i = 0; i < borrowRecords.size(); i++) {
            BorrowRecord borrowRecord = borrowRecords.get(i);
            User user = borrowUsers.get(i);
            BookCopiesVO bookCopiesVO = BookCopiesVO.of(user, borrowRecord);
            copiesVOS.add(bookCopiesVO);
        }
        List<Long> borrowCopiesIds = borrowRecords.stream().map(BorrowRecord::getBookCopiesId).toList();
        log.debug("borrowCopiesIds: {}", borrowCopiesIds);
        copiesIds.removeAll(borrowCopiesIds);
        for (Long copyId : copiesIds) {
            copiesVOS.add(BookCopiesVO.of(copyId));
        }
        return copiesVOS;
    }

    @Override
    @Transactional
    public Book updateBook(UpdateBookRequest request) {
        if (request.getIsbn() != null && existsIsbn(request.getIsbn())) {
            throw new IllegalArgumentException("isbn was exists");
        }
        if (!updateById(request.toBook())) {
            throw new RuntimeException("update book error");
        }
        return getById(request.getId());
    }

    @Override
    @Transactional
    public void addBookCopies(AddBookCopiesRequest request) {
        if (request.getQuantity() > 20) {
            throw new IllegalArgumentException("quantity: " + request.getQuantity() + " is too big");
        }
        Db.saveBatch(request.toBookCopiesList());
    }

    @Override
    @Transactional
    public void removeBookCopies(List<Long> copiesIds) {
        Long count = Db.lambdaQuery(BookCopies.class)
                .in(BookCopies::getId, copiesIds)
                .count();
        if (count != copiesIds.size()) {
            throw new IllegalArgumentException("复本编号不存在");
        }
        Long borrowedCount = Db.lambdaQuery(BorrowRecord.class)
                .eq(BorrowRecord::getStatus, BookStatus.BORROWING)
                .in(BorrowRecord::getBookCopiesId, copiesIds)
                .count();
        if (borrowedCount > 0) {
            throw new IllegalArgumentException("期望下架的图书复本中含有已借出的图书复本");
        }

        if (!bookCopiesService.removeBatchByIds(copiesIds)) {
            throw new RuntimeException("Failed to remove book copies in batch.");
        }
    }

    @Override
    @Transactional
    public void removeBooks(List<Long> bookIds) {
        List<Long> copiesIds = Db.lambdaQuery(BookCopies.class)
                .in(BookCopies::getBookId, bookIds)
                .list().stream().map(BookCopies::getId).toList();
        if (copiesIds.isEmpty()) {
            removeBatchByIds(bookIds);
            return;
        }
        Long borrowedCount = Db.lambdaQuery(BorrowRecord.class)
                .in(BorrowRecord::getBookCopiesId, copiesIds)
                .eq(BorrowRecord::getStatus, BookStatus.BORROWING)
                .count();
        if (borrowedCount > 0) {
            throw new IllegalArgumentException("期望下架的图书中还有借阅中的图书复本");
        }
        //删除复本信息
        bookCopiesService.removeBatchByIds(copiesIds);
        //删除图书信息
        removeBatchByIds(bookIds);
    }

    @Override
    public BookVO queryBookDetails(Long bookId) {
        //查出图书信息
        Book book = getById(bookId);
        if (book == null) {
            throw new IllegalArgumentException("不存在该图书");
        }
        //查出复本信息
        BookVO bookVO = BeanUtil.copyProperties(book, BookVO.class);
        bookVO.setCopies(queryCopiesDetails(bookId));
        return bookVO;
    }
}